package com.trgis.yhmap.util.wmtsproxy.filter;

import com.trgis.yhmap.util.wmtsproxy.WMTSProxyConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 访问规则对象
 * @author wangfy
 * 2022年2月24日 增加日志，统计ip访问次数
 */
@Component
@Scope
@PropertySource(value = "classpath:/filterCon.properties", encoding = "utf-8")
public class AccessRuler {

    private static final Logger log = LoggerFactory.getLogger(AccessRuler.class);

    @Autowired
    WMTSProxyConfiguration wmtsProxyConfiguration;

    private Map<String, IP> ipMap = new HashMap<>();

    /**
     * 规则中使用的时间段，默认300秒
     */
    @Value("${timeLen}")
    private int timeLen = 300;

    /**
     * 规则中规定时间段内的最大访问次数,默认3000次
     */
    @Value("${maxTimes}")
    private int maxTimes = 3000;

    /**
     * 配置刷新时间间隔
     */
    @Value("${refreshTime}")
    private int refreshTime = 20;

    /**
     * IP白名单，不限制访问次数
     */
    private Set<String> bmd;

    private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yy-MM-dd HH:mm:ss");


    /**
     * 根据规则，判断该IP的访问是否继续，当没有超过规定时返回true，否则false
     * @param ip
     * @return
     */
    public boolean checkIP(String ip){
        if (null != bmd && bmd.contains(ip)){
            return true;
        }
        synchronized (ipMap){
            if (!ipMap.containsKey(ip)){
                ipMap.put(ip, new IP(ip));
                log.info("开始记录 {} 的访问次数和时间", ip);
                return true;
            } else {
                IP theIP = ipMap.get(ip);
                if (theIP.getCount() == Long.MAX_VALUE) {
                    log.info("count {} 从 {} 到现在已访问次数 {}",
                            theIP.getIp(),
                            theIP.getInitTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
                                .format(dateTimeFormatter),
                            theIP.getCount()
                    );
                    theIP.initCountAndTime();
                }
                if (theIP.isNotOvertime(timeLen)) {
                    theIP.addOne();
                } else {
                    // 要动态移动开始时间点，把开始时间点移动到适当的时间点
//                    log.info("{} 超出记录的时间段，重设时间段的起点位置", ip);
                    theIP.removeStartTime(timeLen);
                }
                if (theIP.getTimes() < maxTimes) {
                    return true;
                } else {
                    log.info("{} 从 {} 已访问次数 {} 超过规定", ip,
                            theIP.getStartTime().toInstant().atZone(ZoneId.systemDefault())
                                    .toLocalDateTime().format(dateTimeFormatter),
                            theIP.getTimes());
                    return false;
                }
            }
        }
    }

    private ScheduledExecutorService service = Executors.newScheduledThreadPool(4);

    private ScheduledFuture<?> task = null;

    /**
     * 开启定时任务，刷新访问记录，重新读取两个配置文件
     */
    @PostConstruct
    public void start(){
        if (null != task){
            task.cancel(true);
        }
        Runnable run = () -> {
//            log.info("长时间没有访问的从IP监控中移除");
            for(Iterator<Map.Entry<String, IP>> iter = ipMap.entrySet().iterator(); iter.hasNext();){
                Map.Entry<String, IP> entry = iter.next();
                if (entry.getValue().getLastTime().getTime() + timeLen * 1000 < System.currentTimeMillis()){
                    IP ip = entry.getValue();
                    log.info("count {} 从 {} 到现在已访问次数 {}",
                            ip.getIp(),
                            ip.getInitTime().toInstant().atZone(ZoneId.systemDefault())
                                    .toLocalDateTime().format(dateTimeFormatter),
                            ip.getCount());
                    iter.remove();
                }
            }
            wmtsProxyConfiguration.refresh();
            try (InputStream is = new ClassPathResource("filterCon.properties").getInputStream()) {
                Properties pro = new Properties();
                pro.load(is);
                Optional.ofNullable(pro.getProperty("maxTimes")).ifPresent(v -> maxTimes = Integer.parseInt(v));
                Optional.ofNullable(pro.getProperty("timeLen")).ifPresent(v -> timeLen = Integer.parseInt(v));
                Optional.ofNullable(pro.getProperty("refreshTime")).ifPresent(v -> {
                    int nv = Integer.parseInt(v);
                    if (nv - refreshTime != 0) {
                        refreshTime = Integer.parseInt(v);
                        start();
                    }
                    return;
                });
                Optional.ofNullable(pro.getProperty("bmd")).ifPresent(v -> bmd = Arrays.asList(v.split(",")).stream().collect(Collectors.toSet()));
                log.info("配置访问限制规则: 同一IP {} 秒内, 最多访问 {} 次, 配置文件读取刷新间隔 {} 分钟", timeLen, maxTimes, refreshTime);
            } catch (IOException e) {
                log.error("读取配置文件filterCon.properties出错了！");
            }
        };

        task = service.scheduleWithFixedDelay(run, refreshTime, refreshTime, TimeUnit.MINUTES);
    }

}
